package com.hframe.xml;

import com.hframe.data.Base;
import com.hframe.data.DRef;
import com.hframe.type.*;
import com.hframe.typefilter.*;
import org.apache.log4j.Logger;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.dom4j.Namespace;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;


/**
 * Xml转换到对像
 * Created by heqian on 17-9-22.
 */
public class XmlToData implements FilterRef<Object, Object> {
    private static Logger log = Logger.getLogger(XmlToData.class);

    @Override
    public Object filter(Object object) throws Exception {
        return null;
    }


    public static boolean convert(Element root, Base data, String... keys) {
        if (null == root || null == data)
            return false;

        if (data instanceof DRef) {
            for (Iterator<?> i = root.elementIterator(); i.hasNext(); ) {
                Element el = (Element) i.next();
                convert_(el, (DRef<?>) data, keys);
            }

        } else if (data instanceof TData) {
            return convertData(root, (TData) data, keys);

        } else if (data instanceof TArray) {
            return convertArray(root, (TArray) data, keys);

        } else if (data instanceof T) {

        }
//            return setDataValue((Type<?>) data, jsEl.isJsonPrimitive() ? jsEl.getAsJsonPrimitive() : new JsonPrimitive(jsEl.toString()));
        return true;
    }

    private static boolean convert_(Element el, DRef<?> data, String... keys) {
        if (null == el || null == data)
            return false;

        try {
            T<?> object = findField(data, el.getName(), keys);
            if (null != object) {
                return setValue(el, object);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    private static Boolean setValue(Element el, T<?> object) throws Exception {
        XmlFiletr filter = object.getfilter(XmlFiletr.class);
        if (null != filter) {
            filter.set(el.getText());
        } else {
            if (object instanceof TShort) {
                ((TShort) object).set(StringToShort.parse(el.getTextTrim()));

            } else if (object instanceof TLong) {
                ((TLong) object).set(StringToLong.parse(el.getTextTrim()));

            } else if (object instanceof TInt) {
                ((TInt) object).set(StringToInteger.parse(el.getTextTrim()));

            } else if (object instanceof TFloat) {
                ((TFloat) object).set(StringToFloat.parse(el.getTextTrim()));

            } else if (object instanceof TDouble) {
                ((TDouble) object).set(StringToDouble.parse(el.getTextTrim()));

            } else if (object instanceof TChar) {
                ((TChar) object).set(StringToCharacter.parse(el.getTextTrim()));

            } else if (object instanceof TByte) {
                ((TByte) object).set(StringToByte.parse(el.getTextTrim()));

            } else if (object instanceof TBoolean) {
                ((TBoolean) object).set(StringToBoolean.parse(el.getTextTrim()));

            } else if (object instanceof TDate) {
                ((TDate) object).set(StringToDate.parse(el.getTextTrim(), StringToDate.DATE_TIME_LINE));

            } else if (object instanceof TString) {
                ((TString) object).set(el.getTextTrim());

            } else if (object instanceof TBigInteger) {
                if (0 < el.getTextTrim().length())
                ((TBigInteger) object).set(new BigInteger(el.getTextTrim()));

            } else if (object instanceof TBigDecimal) {
                if (0 < el.getTextTrim().length())
                    ((TBigDecimal) object).set(new BigDecimal(el.getTextTrim()));

            } else if (object instanceof TEnum) {
                ((TEnum) object).set(getEnumValue(object, StringToInteger.parse(el.getTextTrim())));

            } else if (object instanceof TData) {
                return convertData(el, (TData) object, null);

            } else if (object instanceof TArray) {
                return convertArray(el, (TArray) object, null);

            } else {
                return false;
            }
        }

        Namespace namespace = el.getNamespace();
        if (null != namespace) {
            object.filter(XmlFiletr.class).setNamespace(namespace.getName(), namespace.getStringValue());
        }

        for (int i = 0; i < el.attributeCount(); i++) {
            Attribute item = el.attribute(i);
            object.filter(XmlFiletr.class).addAttribute(item.getName(), item.getText());
        }
        return true;
    }

    private static boolean convertData(Element root, TData data, String outKey[]) {
        try {
            Base object = (Base) data.getClassName().newInstance();
            outKey = getOutkey(data, outKey);
            convert(root, object, outKey);

            data.set(object);
            return true;
        } catch (Exception e) {
            log.error(e.toString(), e);
            return false;
        }
    }

    private static String[] getOutkey(T data, String[] outKey) {
        if (null == outKey || 0 == outKey.length) {
            XmlFiletr filetr = (XmlFiletr) data.getfilter(XmlFiletr.class);
            if (null != filetr)
                outKey = filetr.getKeys();
            if (null == outKey)
                outKey = new String[0];
        }
        return outKey;
    }

    private static boolean convertArray(Element root, TArray data, String outKey[]) {
        try {
            Base object = (Base) data.getClassName().newInstance();
            outKey = getOutkey(data, outKey);
            convert(root, object, outKey);
            data.add(object);
            return true;
        } catch (Exception e) {
            log.error(e.toString(), e);
            return false;
        }
    }

    protected static T<?> findField(DRef<?> data, String key, String... keys) {
        if (null == key || 0 >= key.trim().length())
            return null;

        Class<?> cla = data.getClass();
        while (null != cla && DRef.class != cla) {
            for (Field field : cla.getDeclaredFields()) {
                field.setAccessible(true);
                try {
                    if (!isExtendsClass(field.getType(), T.class))
                        continue;

                    String fieldName = field.getName();
                    if (keys.length > 0 && !isOut(fieldName, keys))
                        return null;

                    Object object = field.get(data);

                    if (null == object)
                        continue;

                    T<?> typeObj = (T<?>) object;
                    XmlFiletr filter = typeObj.getfilter(XmlFiletr.class);
                    if (null != filter) {
                        if (filter.getReadWait() == XmlFiletr.ReadWait.OnlyRead || filter.getReadWait() == XmlFiletr.ReadWait.NotReadWait)
                            return null;

                        fieldName = filter.getName();
                    }

                    if (null == fieldName)
                        fieldName = field.getName();

                    if (key.toLowerCase().equals(fieldName.toLowerCase())) {
                        return typeObj;
                    }

                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            cla = cla.getSuperclass();
        }
        return null;
    }

    private static boolean isOut(String name, String... keys) {
        for (String key : keys) {
            if (key.toLowerCase().equals(name.toLowerCase()))
                return true;
        }
        return false;
    }

    private static Enum getEnumValue(T<?> typeObj, Integer val) throws IllegalAccessException {
        if (null == val)
            return null;

        Class<Enum> className = (Class<Enum>) typeObj.getClassName();
        if (null != className && null != val) {
            Field[] fields = className.getDeclaredFields();
            for (Field field : fields) {
                if (!field.isEnumConstant())
                    continue;

                Object object = field.get(null);
                if (((Enum) object).ordinal() == val) {
                    return (Enum) object;
                }
            }
        }
        return null;
    }

    protected static boolean isExtendsClass(Class<?> src, Class<?> dis) {
        while (src != null) {
            if (src == dis)
                return true;
            src = src.getSuperclass();
        }
        return false;
    }

}
